package bus
import chisel3._
import chisel3.util._
import utils._
import common.Constants._
import yycore.LSUOpType

class CoreLinkIOtoAXI4Converter[OT <: AXI4Lite](outType: OT, isFromCache: Boolean, len: Int) extends Module {
  val io = IO(new Bundle {
    val in = Flipped(new CoreLinkIO(XLEN))
    val out = Flipped(Flipped(outType))
  })

  val toAXI4Lite = (outType.getClass == classOf[AXI4Lite]).B
  val toAXI4 = (outType.getClass == classOf[AXI4]).B
  assert(toAXI4Lite || toAXI4)

  val (in, axi) = (io.in, io.out)
  val (ar, aw, w, r, b) = (axi.ar.bits, axi.aw.bits, axi.w.bits, axi.r.bits, axi.b.bits)

  val size = in.req.bits.size

  ar.addr  := in.req.bits.addr
  //ar.prot  := AXI4Parameters.PROT_PRIVILEDGED
  w.data := in.req.bits.wdata
  w.strb := LSUOpType.MakeMask(addr = in.req.bits.addr, size = size)


  def LineBeats = len
  assert(len > 0)
  val wlast = WireInit(true.B)
  val rlast = WireInit(true.B)
  if (outType.getClass == classOf[AXI4]) {
    val axi4 = io.out.asInstanceOf[AXI4]
    axi4.ar.bits.id    := 0.U
//    axi4.ar.bits.region    := 0.U
    axi4.ar.bits.len   := 0.U//Mux(in.req.bits.isBurst(), (LineBeats - 1).U, 0.U)
    axi4.ar.bits.size  := in.req.bits.size
    axi4.ar.bits.burst := (if (isFromCache) AXI4Parameters.BURST_WRAP
                           else AXI4Parameters.BURST_INCR)
//    axi4.ar.bits.lock  := false.B
//    axi4.ar.bits.cache := 0.U
//    axi4.ar.bits.qos   := 0.U
//    axi4.ar.bits.user  := 0.U
    axi4.w.bits.last   := true.B  //in.req.bits.isWriteLast()
    wlast := axi4.w.bits.last
    rlast := axi4.r.bits.last
  }

  aw := ar
  in.resp.bits.rdata := r.data
  in.resp.bits.cmd  := Mux(rlast, LinkBusCmd.readLast, 0.U)

  val wSend = Wire(Bool())
  val awAck = BoolStopWatch(axi.aw.fire(), wSend)
  val wAck = BoolStopWatch(axi.w.fire() && wlast, wSend)
  wSend := (axi.aw.fire() && axi.w.fire() && wlast) || (awAck && wAck)
  val wen = RegEnable(in.req.bits.isWrite(), in.req.fire())

  axi.ar.valid := in.isRead
  axi.aw.valid := in.isWrite && !awAck
  axi.w.valid := in.isWrite && !wAck
  in.req.ready  := Mux(in.req.bits.isWrite(), !wAck && axi.w.ready, axi.ar.ready)

  axi.r.ready  := in.resp.ready
  axi.b.ready  := in.resp.ready
  in.resp.valid  := Mux(wen, axi.b.valid, axi.r.valid)
}

object CoreLinkIOtoAXI4Converter {
  def apply[OT <: AXI4Lite](in: CoreLinkIO, outType: OT, isFromCache: Boolean = false, len: Int = 1): OT = {
    val toAXIbridge = Module(new CoreLinkIOtoAXI4Converter(outType, isFromCache, len: Int))
    toAXIbridge.io.in <> in
    toAXIbridge.io.out
  }
}